package security

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
)

// AuditAction represents an audited action
type AuditAction string

const (
	AuditActionCreate AuditAction = "create"
	AuditActionRead   AuditAction = "read"
	AuditActionUpdate AuditAction = "update"
	AuditActionDelete AuditAction = "delete"
	AuditActionAuth   AuditAction = "auth"
	AuditActionAdmin  AuditAction = "admin"
)

// AuditEvent represents an audit event
type AuditEvent struct {
	ID        string                 `json:"id"`
	Timestamp time.Time              `json:"timestamp"`
	Action    AuditAction            `json:"action"`
	Principal string                 `json:"principal"`
	Resource  string                 `json:"resource"`
	Status    string                 `json:"status"`
	Details   map[string]interface{} `json:"details,omitempty"`
	Error     string                 `json:"error,omitempty"`
	ClientIP  string                 `json:"client_ip,omitempty"`
	UserAgent string                 `json:"user_agent,omitempty"`
	RequestID string                 `json:"request_id,omitempty"`
}

// AuditLogger handles audit logging
type AuditLogger struct {
	logger *logrus.Logger
}

// NewAuditLogger creates a new audit logger
func NewAuditLogger(logger *logrus.Logger) *AuditLogger {
	return &AuditLogger{
		logger: logger,
	}
}

// Log logs an audit event
func (l *AuditLogger) Log(ctx context.Context, event *AuditEvent) error {
	if event.ID == "" {
		event.ID = uuid.NewString()
	}
	if event.Timestamp.IsZero() {
		event.Timestamp = time.Now()
	}

	// Convert event to JSON for structured logging
	data, err := json.Marshal(event)
	if err != nil {
		return fmt.Errorf("failed to marshal audit event: %w", err)
	}

	// Log with appropriate level based on status
	fields := logrus.Fields{
		"audit_id":     event.ID,
		"audit_action": event.Action,
		"principal":    event.Principal,
		"resource":     event.Resource,
		"status":       event.Status,
	}

	if event.Error != "" {
		l.logger.WithFields(fields).Error(string(data))
	} else {
		l.logger.WithFields(fields).Info(string(data))
	}

	return nil
}

// LogSuccess logs a successful audit event
func (l *AuditLogger) LogSuccess(ctx context.Context, action AuditAction, principal string, resource string, details map[string]interface{}) error {
	event := &AuditEvent{
		Action:    action,
		Principal: principal,
		Resource:  resource,
		Status:    "success",
		Details:   details,
	}
	return l.Log(ctx, event)
}

// LogError logs a failed audit event
func (l *AuditLogger) LogError(ctx context.Context, action AuditAction, principal string, resource string, err error, details map[string]interface{}) error {
	event := &AuditEvent{
		Action:    action,
		Principal: principal,
		Resource:  resource,
		Status:    "error",
		Error:     err.Error(),
		Details:   details,
	}
	return l.Log(ctx, event)
}

// LogAuth logs an authentication event
func (l *AuditLogger) LogAuth(ctx context.Context, principal string, success bool, err error, details map[string]interface{}) error {
	status := "success"
	var errStr string
	if !success {
		status = "error"
		if err != nil {
			errStr = err.Error()
		}
	}

	event := &AuditEvent{
		Action:    AuditActionAuth,
		Principal: principal,
		Resource:  "auth",
		Status:    status,
		Error:     errStr,
		Details:   details,
	}
	return l.Log(ctx, event)
}

// LogAdmin logs an administrative action
func (l *AuditLogger) LogAdmin(ctx context.Context, principal string, resource string, details map[string]interface{}) error {
	event := &AuditEvent{
		Action:    AuditActionAdmin,
		Principal: principal,
		Resource:  resource,
		Status:    "success",
		Details:   details,
	}
	return l.Log(ctx, event)
}
